package com.lin.filegraph.utils.filedg;

import com.lin.filegraph.utils.parser.node.JFileNode;
import com.lin.filegraph.utils.parser.node.JProjectNode;
import com.lin.filegraph.utils.parser.simpleast.SimpleASTHelper;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @description:
 * @author: linhuaixu
 * @time: 2023/5/18 10:04
 */

public class FileGraph {

    private List<FileGraphNode> listOfFiles;
    private List<FileGraphEdge> listOfFileEdges;

    public FileGraph(JProjectNode root) {
        this(new ArrayList<FileGraphNode>());
        List<JFileNode> jfNodes = SimpleASTHelper.findAllFilesInSubTree(root);
        for (JFileNode fileNode : jfNodes) {
            listOfFiles.add(new FileGraphNode(fileNode));
        }
    }


    public FileGraph(List<FileGraphNode> fileNodes) {
        this(fileNodes, new ArrayList<FileGraphEdge>());
    }

    public FileGraph(List<FileGraphNode> fileNodes, List<FileGraphEdge> subEdges) {
        this.listOfFiles = fileNodes;
        this.listOfFileEdges = subEdges;
    }

    public void addEdge(FileGraphEdge newEdge) {
        listOfFileEdges.add(newEdge);
    }

    public void addNode(FileGraphNode newNode) {
        listOfFiles.add(newNode);
    }

    public List<FileGraphNode> getListOfFiles() {
        return listOfFiles;
    }

    public List<FileGraphEdge> getListOfFileEdges() {
        return listOfFileEdges;
    }

    public void setListOfFiles(List<FileGraphNode> listOfFiles) {
        this.listOfFiles = listOfFiles;
    }

    public List<FileGraphNode> getPreFiles(FileGraphNode currentFile) {
        List<FileGraphNode> preFiles = new ArrayList<>();
        for (FileGraphEdge edge : listOfFileEdges) {
            if (edge.getPostFile().equals(currentFile)) {
                preFiles.add(edge.getPreFile());
            }
        }
        return preFiles;
    }

    public List<FileGraphNode> getPostFiles(FileGraphNode currentFile) {
        List<FileGraphNode> postFiles = new ArrayList<>();
        for (FileGraphEdge edge : listOfFileEdges) {
            if (edge.getPreFile().equals(currentFile)) {
                postFiles.add(edge.getPostFile());
            }
        }
        return postFiles;
    }

    public List<String> getPreFileRelPaths(String currentFileRelPath) {
        List<String> preFiles = new ArrayList<>();
        for (FileGraphEdge edge : listOfFileEdges) {
            if (edge.getPostFile().getRelativePath().equals(currentFileRelPath)) {
                preFiles.add(edge.getPreFile().getRelativePath());
            }
        }
        return preFiles;
    }

    public List<String> getPostFileRelPaths(String currentFileRelPath) {
        List<String> postFiles = new ArrayList<>();
        for (FileGraphEdge edge : listOfFileEdges) {
            if (edge.getPreFile().getRelativePath().equals(currentFileRelPath)) {
                postFiles.add(edge.getPostFile().getRelativePath());
            }
        }
        return postFiles;
    }

    public FileGraph getSubFDG(List<FileGraphNode> subFiles) {

        List<FileGraphEdge> subEdges = new ArrayList<>();
        for (FileGraphEdge edge : listOfFileEdges) {
            if (subFiles.contains(edge.getPreFile()) && subFiles.contains(edge.getPostFile())) {
                subEdges.add(edge);
            }
        }
        FileGraph subFDG = new FileGraph(subFiles, subEdges);
        return subFDG;
    }

    public FileGraph getSubFDGByFileStrs(List<String> subFiles) {


        List<FileGraphNode> subNodes = new ArrayList<>();
        for (FileGraphNode node : listOfFiles) {
            if (subFiles.contains(node.getRelativePath())) {
                subNodes.add(node);
            }
        }
        return getSubFDG(subNodes);
    }

    public int getFileGraphNodesNumber() { return this.listOfFiles.size();}

    public List<FileGraphNode> getFileGraphNode() {
        return this.listOfFiles;
    }

    public FileGraphNode getFileGraphNode(String filePath) {

        for (FileGraphNode node : listOfFiles) {
            if (node.getAbsolutePath().equals(filePath)) {
                return node;
            }
        }
        return null;
    }

    public List<FileGraphEdge> getFileGraphEdge() {
        return this.listOfFileEdges;
    }

    public FileGraphEdge getFileGraphEdge(String filePath1, String filePath2) {

        for (FileGraphEdge edge : listOfFileEdges) {
            if (edge.getPreFile().getAbsolutePath().equals(filePath1)
                    && edge.getPostFile().getAbsolutePath().equals(filePath2)) {
                return edge;
            }
        }
        return null;
    }

    public void display() {
        System.out.println("文件数量:" + listOfFiles.size());

        for (FileGraphNode file : listOfFiles) {
            System.out.println(file.getFileName() + "." + file.getFileType());
        }
        System.out.println("文件依赖关系如下:");
        for (FileGraphEdge edge : listOfFileEdges) {
            System.out.print("[FileEdge] " + edge.getPreFile().getFileName() + "." + edge.getPreFile().getFileType() + "  -->  " + edge.getPostFile().getFileName() + "." + edge.getPostFile().getFileType() + " ");
            System.out.println(edge.getCoupleDetail());
        }

    }
    public void display2() {
        if (listOfFiles.size() < 30) {
            for (FileGraphNode file : listOfFiles) {
                System.out.println(file.getFileName() + "." + file.getFileType() + " " + file.getRelativePath());
            }
        }
    }

    public void filterloc(int loc) {
        this.listOfFiles.removeIf(node -> node.getValidLOC() < loc);
        FileGraph subFDG = this.getSubFDG(this.listOfFiles);
        this.listOfFileEdges = subFDG.listOfFileEdges;
    }

    public void filterTest() {
        this.listOfFiles.removeIf(node -> node.getRelativePath().toUpperCase().contains("TEST"));
        FileGraph subFDG = this.getSubFDG(this.listOfFiles);
        this.listOfFileEdges = subFDG.listOfFileEdges;
    }

    public void filterDepend(String rules) {
        List <CouplingType> unrules = getDependRules(rules);
        for (FileGraphEdge edge : this.getListOfFileEdges()) {
            edge.getCouplingInfo().removeCouplingCount(unrules);
        }
        Iterator<FileGraphEdge> edges = this.listOfFileEdges.iterator();
        while(edges.hasNext()) {
            FileGraphEdge edge = edges.next();
            boolean hasValue = false;
            for (CouplingType ct : edge.getMapOfCouplingCounter().keySet()) {
                if (edge.getCouplingInfo().getCouplingCountByType(ct) != 0) {
                    hasValue = true;
                }
            }
            if (!hasValue) {
                edges.remove();
            }
        }
    }

    public List<CouplingType> getDependRules(String rules){
        List<CouplingType> types = new ArrayList<CouplingType>(){
            {
                this.add(CouplingType.valueOf("Inheritance"));
                this.add(CouplingType.valueOf("Implements"));
                this.add(CouplingType.valueOf("Parameter"));
                this.add(CouplingType.valueOf("Instantiation"));
                this.add(CouplingType.valueOf("DeclareToDefine"));
                this.add(CouplingType.valueOf("Call"));
                this.add(CouplingType.valueOf("Return"));
                this.add(CouplingType.valueOf("Combination"));
                this.add(CouplingType.valueOf("DeclarationType"));
            }
        };
        List<CouplingType> rule = new ArrayList<CouplingType>(){
            {
                if(String.valueOf(rules.charAt(0)).equals("1")) {
                    this.add(CouplingType.valueOf("Inheritance"));
                }
                if(String.valueOf(rules.charAt(1)).equals("1")) {
                    this.add(CouplingType.valueOf("Implements"));
                }
                if(String.valueOf(rules.charAt(2)).equals("1")) {
                    this.add(CouplingType.valueOf("Parameter"));
                }
                if(String.valueOf(rules.charAt(3)).equals("1")) {
                    this.add(CouplingType.valueOf("Instantiation"));
                }
                if(String.valueOf(rules.charAt(4)).equals("1")) {
                    this.add(CouplingType.valueOf("DeclareToDefine"));
                }
                if(String.valueOf(rules.charAt(5)).equals("1")) {
                    this.add(CouplingType.valueOf("Call"));
                }
                if(String.valueOf(rules.charAt(6)).equals("1")) {
                    this.add(CouplingType.valueOf("Return"));
                }
                if(String.valueOf(rules.charAt(7)).equals("1")) {
                    this.add(CouplingType.valueOf("Combination"));
                }
                if(String.valueOf(rules.charAt(8)).equals("1")) {
                    this.add(CouplingType.valueOf("DeclarationType"));
                }
            }
        };
        types.removeAll(rule);
        return types;
    }

//    public String toString() {
//        String str = "文件数量:" + listOfFiles.size() + "\n";
//        for (FileGraphNode file : listOfFiles) {
//            str += file + "\n";
//        }
//        str += "文件依赖关系如下:" + "\n";
//        for (FileGraphEdge edge : listOfFileEdges) {
//            str += edge + "\n";
//        }
//        return str;
//    }

    // suxiaowei
    FileGraphNode find_cfilenodebypath(String path) {
        for (FileGraphNode filenode : listOfFiles) {
            if (filenode.getAbsolutePath().trim().equals(path))
                return filenode;
        }
        return null;
    }


}

